<?php
/**
 +------------------------------------------------------------------------------
 * model for ePHP
 * model是底层数据库驱动的兼容层，直接调用数据库驱动
 +------------------------------------------------------------------------------
 * @version 3.0
 * @author  WangXian
 * @package core
 * @creation date 2010-8-12
 * @modified date 2011.06.11
 +------------------------------------------------------------------------------
 */

class model
{
	private $table_name = '';
	private $field		= '*';
	private $orderby	= '';
	private $groupby 	= '';
	private $limit 		= '';
	private $where		= '';
	private $data		= array();
	
	private $query_sql		= '';		#用query($sql)方法，直接用SQL进行查询。
	public  $db_config_name = 'default';#默认的使用的数据库

	private $db = NULL;
	static private $_db_handle;
//	public function __construct()
//	{}

	/**
	 * 连接数据库,xxModel中也可以调用
	 * 只有2种情况下才去连接数据库。 1.未数据库连接; 2.需要连接别的DB SERVER
	 * @access protected
	 * @return object $db 数据库驱动操作对象
	 */
	protected function conn()
	{
		if(! isset(self::$_db_handle[$this->db_config_name]) )
		{
			$dbdriver = 'db_'.C('dbdriver');
			include_once FW_PATH.'/dbdrivers/'. $dbdriver .'.class.php';
			self::$_db_handle[$this->db_config_name] = $this->db = new $dbdriver($this->db_config_name);
		}
		else $this->db = self::$_db_handle[$this->db_config_name];
		return $this->db;
	}

	/**
	 * 生成SELECT类型的SQL语句。
	 * @access private
	 * @return string $sql 查询sql语句
	 */
	private function _read_sql()
	{
		//可能是model::query('select')，直接接受SQL的查询操作
		if( ! $this->query_sql )
		{
			$_table_name = $this->_get_table_name();
	
			$_where   = empty($this->where) ? '' : ' WHERE '.$this->where;
			$_orderby = empty($this->orderby) ? '' : ' ORDER BY '.$this->orderby;
			$_groupby = empty($this->groupby) ? '' : ' GROUP BY '.$this->groupby;
			$_limit   = empty($this->limit) ? '' : ' LIMIT '.$this->limit;
	
			$this->db->sql = 'SELECT '. $this->field .' FROM '.$_table_name.$_where.$_groupby.$_orderby.$_limit;
			
			$this->_clear_var(); //清理使用过的变量
			return $this->db->sql;
		}
		else
		{
			$this->db->sql = $this->query_sql;
			$this->query_sql = '';
			return $this->db->sql;
		}
	}
	
	/**
	 * 获取表名
	 * @access private
	 * @return string $table_name 表名
	 */
	private function _get_table_name()
	{
		if( $this->table_name == '' )
		{
			//如果是在实例化后使用，则使用当前模型名称
			$current_class = get_class($this);
			if($current_class != 'model')
			{
				$this->table_name = substr($current_class,0,strlen($current_class) - 5);
				
				//如果设置了表前缀
				if(true == ($tb_prefix = C($this->db_config_name.'.tb_prefix','db') ) ) $this->table_name = $tb_prefix.$this->table_name;
			}
			else throw new ephpException('table_name无法确定！');
		}

		return $this->table_name;
	}


	/**
	 * 清理使用过的变量
	 * @access private
	 * @return void
	 */
	private function _clear_var()
	{
		//$this->table_name = '';
		$this->field 	= '*';
		$this->orderby 	= '';
		$this->groupby 	= '';
		$this->limit 	= '';
		$this->where 	= '';
	}
	
	/**
	 * 查询的表名
	 * @param  string $table_name 表名
	 * @return string $this
	 */
	public function table($table_name)
	{
		$this->table_name = $table_name;
		return $this;
	}

	/**
	 * model::table()方法的别名，查询的表名
	 * @param string $table_name
	 * @return $this
	 */
	public function from($table_name)
	{ return $this->table($table_name); }
	
	/**
	 * 指定使用db.config.php哪个数据库配制帐号，例如default、primary
	 * @param string $db_config_name
	 * @return object $this
	 */
	public function dbconfig($db_config_name)
	{
		$this->db_config_name = $db_config_name;
		return $this;
	}

	/**
	 * 要查询的数据库字段，也就是SQL中select后的字段列表
	 * @param  string $field 要查询的字段列表
	 * @return object $this
	 */
	public function select($field)
	{
		$this->field = $field;
		return $this;
	}
	
	/**
	 * select()的别名，要查询的数据库字段
	 * @param string $field 要查询的字段列表
	 * @return object $this
	 */
	public function field($field)
	{ return $this->select($field); }
	
	/**
	 * SQL中limit，使用方法：model::limit('0,10') 或 model::limit(0,10)
	 * @param mixed $limit limit部分或开始部分
	 * @param integer $pagecount 每页数目
	 * @return object $this
	 */
	public function limit($limit,$pagecount=0)
	{
		if( ! $pagecount ) $this->limit = $limit;
		else $this->limit = $limit.','.$pagecount;
		return $this;
	}


	/**
	 * 写入数据库的内容(insert|update)
	 * @param array $data 要写入数据库的内容。
	 * @param array $noquote data中不加引号的字段列表。如array('updated','create_time');
	 * @return object $this
	 */
	public function set($data,$noquote=array())
	{
		if(is_array($data))
		{
			foreach ($data as $k=>$v)
			{
				if(empty($noquote) || ! in_array($k,$noquote) )
				{
					$v = $this->escape_string($v);
					$data[$k] = "'{$v}'";
				}
			}
		}
		else throw new ephpException('model::set($data,$noquote)或model::data($data,$noquote)，第一个和第二个参数都必须是一个数组。');

		$this->data = $data;
		return $this;
	}
	
	/**
	 * model::set()方法的别名。
	 * @param array $data 要写入数据库的内容。
	 * @param array $noquote data中不加引号的字段列表。如array('updated','create_time');
	 * @return object $this
	 */
	public function data($data,$noquote=array())
	{ return $this->set($data,$noquote); }
	
	/**
	 * SQL中的where条件
	 * @param  string||array $where 可以是一个字符串或数组。
	 * @param  array $noquote 指定那些字段不加引号引号。如array('updated','create_time');
	 * @return object $this
	 */
	public function where($where,$noquote=array())
	{
		//如果是$where是string，则直接返回
		if(is_string($where))
		{
			$this->where = $where;
			return $this;
		}

		//生成SQL中where的部分
		if(is_array($where))
		{
			$tmp = array();
			foreach ($where as $k=>$v)
			{
				if(empty($noquote) && ! in_array($k,$noquote) )
				{
					$v = $this->escape_string($v);
					$tmp[] = $k."='".$v."'";
				}
				else $tmp[] = $k."=".$v; //不加引号。
			}
			$this->where = implode(' AND ', $tmp);
		}
		else throw new ephpException('model::where($where,$noquote) 第二个参数$noquote必须是一个数组。');
		return $this;
	}
	
	/**
	 * order by排序
	 * @param  string $orderby
	 * @return string $this
	 */
	public function orderby($orderby)
	{
		$this->orderby = $orderby;
		return $this;
	}
	
	/**
	 * SQL group by
	 * @param string $groupby 分组
	 * @return object $this
	 */
	public function groupby($groupby)
	{
		$this->groupby = $groupby;
		return $this;
	}
	
	/**
	 * 返回影响的数据行数。
	 * @return integer $affected_rows
	 */
	public function affected_rows()
	{ return $this->db->affected_rows();}

	/**
	 * 返回刚插入的insert_id
	 * @return integer $insert_id
	 */
	public function insert_id()
	{ return $this->db->insert_id(); }
	
	/**
	 * 最后执行的sql
	 * @return string $sql
	 */
	public function getLastSql()
	{ return $this->db->sql; }
	
////////////////////////////////////////////////////////////////////////////////////////////////////////
// CURD操作
////////////////////////////////////////////////////////////////////////////////////////////////////////

	/**
	 * 调用数据库db::query方法，如果是SELECT可以后后续操作。
	 * @param  string $sql 要查询的SQL或执行commit的SQL等。
	 * @return mixed
	 */
	public function query($sql)
	{
		#鉴定是执行查询还是commit提交操作。如果是select，可以有后续操作。
		if(strtolower(substr($sql,0,6)) == 'select')
		{
			$this->query_sql = $sql;
			return $this;
		}
		else return $this->conn()->query($sql);
	}
	
	#------------------------------------------------------------------------------查询操作

	/**
	 * 以array的方式,返回查询到的一条数据
	 * @param string $table_name 表名
	 * @return array $data
	 */
	public function find( $table_name='' )
	{//到最后一步才去连接数据库，减小类开销
		if( $table_name ) $this->table_name = $table_name;
		return $this->conn()->fetch_array($this->_read_sql());
	}
	
	/**
	 * 以array的方式,返回多条查询结果
	 * @param string $table_name 表名
	 * @return array $data
	 */
	public function findAll( $table_name='' )
	{
		if( $table_name ) $this->table_name = $table_name;
		return $this->conn()->fetch_arrays($this->_read_sql());
	}


	/**
	 * 以object的方式,返回查询到的一条数据
	 * @param string $table_name 表名
	 * @return object $data
	 */
	public function findObj( $table_name='' )
	{
		if( $table_name ) $this->table_name = $table_name;
		return $this->conn()->fetch_object($this->_read_sql());
	}

	/**
	 * 以object的方式,返回查询到的多条数据
	 * @param string $table_name 表名
	 * @return object $data
	 */
	public function findObjs( $table_name='' )
	{
		if( $table_name ) $this->table_name = $table_name;
		return $this->conn()->fetch_objects($this->_read_sql());
	}
	
	/**
	 * 统计数据条数
	 * 示例:$m->table('test')->select('id')->where('id<12')->count();
	 * @return integer $count
	 */
	public function count()
	{
		$_table_name = $this->_get_table_name();
		$_where = ($this->where != '') ? ' WHERE '. $this->where : '';
		$_field = $this->field;
		
		//清理使用过的变量
		$this->where = '';
		$this->field = '*';

		$this->conn();
		$this->db->sql = 'SELECT count('. $_field .') count FROM '.$_table_name.$_where;
		return $this->db->fetch_object( $this->db->sql )->count;
	}

	/**
	 * 和findAll()差不多，不过返回的内容中包含不带limit的所有数据。只支持mysql数据库
	 * 返回的数据结构：array('data'=>array(....), 'data_count'=>总数据数)
	 * @param string $table_name
	 * @return array $data 结构：array('data'=>array(....), 'data_count'=>10)
	 */
	public function findPage( $table_name='' )
	{
		if( $table_name ) $this->table_name = $table_name;
		
		$dbdriver = C('dbdriver');
		if($dbdriver == 'mysql' || $dbdriver == 'mysqli')
		{// mysql支持select SQL_CALC_FOUND_ROWS xx from xxx where统计查询的数据数。
			$this->field = 'SQL_CALC_FOUND_ROWS '. $this->field;
			
			$ret_data['data'] = $this->findAll();
			$_tmp_sql = $this->db->sql;//本次执行的sql。
			
			$ret_data['data_count'] =  $this->db->fetch_object('SELECT FOUND_ROWS() count')->count;
			$this->db->sql = $_tmp_sql;//让上次的sql作为最后执行的sql语句。
			return $ret_data;
		}
		else
		{
			$_table_name = $this->_get_table_name();
			$_where = ($this->where != '') ? ' WHERE '. $this->where : '';

			$this->conn();
			$this->db->sql = 'SELECT count(*) count FROM '.$_table_name.$_where;
		
			$ret_data['data_count'] = $this->db->fetch_object( $this->db->sql )->count;
			$ret_data['data'] = $this->findAll();
			return $ret_data;
		}
	}

	#------------------------------------------------------------------------------insert update delete操作
	
	/**
	 * 删除一行或多行，如果希望删除所有行,请将$f置为true，默认false
	 * @param boolean $f
	 * @return boolean
	 */
	public function delete($f=false)
	{
		$_where = '';
		$_table_name = $this->_get_table_name();
		
		if( $this->where ) $_where = ' WHERE '.$this->where;
		else if($this->where == '' && $f == false) throw new ephpException('警告：执行delete需要有where条件，似乎你漏掉了where条件，如果您确认不使用where条件，请把使用model::delete(true)');
			
		//清理使用过的变量
		$this->where = '';
		
		$this->conn();
		$this->db->sql = 'DELETE FROM '.$_table_name.$_where;
		return $this->db->query( $this->db->sql );
	}
	
	/**
	 * 更新数据库一行或多行，如果希望更新所有的数据，即无where条件，请将$f置为true，默认false
	 * @param boolean $f
	 * @return boolean
	 */
	public function update($f=false)
	{
		if(empty($this->data)) throw new ephpException('警告：您没有往数据库中写入任何信息。请使用model::set(array $data)指定写入数据库的数据。');
		
		$_where = '';
		$_table_name = $this->_get_table_name();
		
		$_set_string = ' SET ';
		$tmp = array();
		foreach ($this->data as $k=>$v) $tmp[] = $k.'='.$v;
		$_set_string .= implode(',', $tmp);
		
		if( $this->where ) $_where = ' WHERE '.$this->where;
		else if($this->where == '' && $f == false) throw new ephpException('警告：执行update需要有where条件，似乎你漏掉了where条件，如果您确认不使用where条件，请把使用model::update(true)');
		
		
		//清理使用过的变量
		$this->data = array();
		$this->where = '';

		$this->conn();
		$this->db->sql='UPDATE '.$_table_name.$_set_string.$_where;
		return $this->db->query($this->db->sql);
	}

	/**
	 * 把数据写入数据库。
	 * @return integer $insert_id
	 */
	public function insert()
	{
		if(empty($this->data)) throw new ephpException('警告：您没有往数据库中写入任何信息。请使用model::set(array $data)指定写入数据库的数据。');
				
		$_table_name = $this->_get_table_name();
		$_fields = implode(',', array_keys($this->data));
		$_values = implode(',', array_values($this->data));

		//清理使用过的变量
		$this->data=array();
		
		$this->conn();
		$this->db->sql='INSERT INTO '. $_table_name ." ({$_fields}) VALUES ({$_values})";
		$this->db->query( $this->db->sql );
		return $this->db->insert_id();
	}

	/**
	 * model::insert()的别名。
	 * @return integer $insert_id
	 */
	public function add()
	{ return $this->insert(); }
	
	/**
	 * 过滤SQL中的不安全字符
	 * @param string $str
	 */
	public function escape_string($str)
	{
		return $this->conn()->escape_string($str);
	}
	
}
/* End of file model.class.php */
/* Location: ./_framework/libraries/model.class.php */